#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <functional>
#include <algorithm>

#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#include "InetAddr.hpp"
#include "UdpServer.hpp"

class MessageRouter
{
public:
    bool addUser(const InetAddr& user)
    {
        pthread_mutex_lock(&_mutex);
        for (auto& o_user : _online_user)
        {
            if (user == o_user)
            {
                pthread_mutex_unlock(&_mutex);
                return false;
            }
        }

        _online_user.push_back(user);
        pthread_mutex_unlock(&_mutex);
        return true;
    }

    bool delUser(const InetAddr& user)
    {
        pthread_mutex_lock(&_mutex);
        auto it = find(_online_user.begin(), _online_user.end(), user);

        if (it == _online_user.end())
        {
            pthread_mutex_unlock(&_mutex);
            return false;
        }

        _online_user.erase(it);
        pthread_mutex_unlock(&_mutex);
        return true;
    }

    struct SendPackage
    {
        SendPackage(MessageRouter* self, int sockfd, std::string message, InetAddr cliAddr)
            : _self(self)
            , _sockfd(sockfd)
            , _message(message)
            , _cliAddr(cliAddr)
        {}

        MessageRouter* _self;
        int _sockfd;
        std::string _message;
        InetAddr _cliAddr;
    };

    static void* messageSender(void* args)
    {
        SendPackage* sendpkg = (SendPackage*)args;

        std::string msg = "[" + sendpkg->_cliAddr.ip() + ":"
                            + std::to_string(sendpkg->_cliAddr.port()) + "]"
                            + sendpkg->_message;

        std::cout << "sending..." << msg << std::endl;

        pthread_mutex_lock(&sendpkg->_self->_mutex);
        for (auto& usr : sendpkg->_self->_online_user)
        {
            struct sockaddr_in cliaddr = usr.addr();
            sendto(sendpkg->_sockfd, msg.c_str(), msg.size(), 0, (sockaddr*)&cliaddr, sizeof(cliaddr));
        }

        pthread_mutex_unlock(&sendpkg->_self->_mutex);
        delete sendpkg;
        return nullptr;
    }

    void router(int sockfd, std::string message, InetAddr cliAddr)
    {
        // 首次发消息 -> 注册
        addUser(cliAddr);

        // 用户退出
        if (message == "/quit")
        {
            delUser(cliAddr);
            return;
        }

        // 线程转发消息
        SendPackage* sendpkg = new SendPackage(this, sockfd, message, cliAddr);
        pthread_t tid;
        pthread_create(&tid, 0, messageSender, sendpkg);
        pthread_detach(tid);
    }

private:
    std::vector<InetAddr> _online_user;
    pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;
};